package com.fooddiary.service.impl;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.fooddiary.dao.IModelDao;
import com.fooddiary.dao.IRoleDao;
import com.fooddiary.dao.IUserDao;
import com.fooddiary.model.Model;
import com.fooddiary.model.Role;
import com.fooddiary.model.User;
import com.fooddiary.service.IUserService;
import com.fooddiary.util.AppConstants;
import com.fooddiary.util.AppException;
import com.fooddiary.web.form.RegistrationForm;
import com.fooddiary.web.form.UserProfileForm;

/**
 * Implements operations for user (registration, changing profile, e.t.s)
 * @author Created by andrey.kuznetsov <br>
 * @author Last modified by $Author$ <br>
 * @author Last modified on $Date$ at revision $Revision$ <br>
 */
@Service
public class UserService extends AbstractService implements IUserService{
	
	private final Boolean MODE_REGISTRATION = true;
	private final Boolean MODE_USERPROFILE = false;
	
	
	@Autowired 
	private IUserDao userDao;
	
	@Autowired
	private IModelDao modelDao;

	@Autowired 
	private IRoleDao roleDao;
	
	@Override
	@Transactional(readOnly=false)
	public RegistrationForm registerNewUser(RegistrationForm p_form) throws Exception {
		validateRegistrationForm( p_form );
		
		if(CollectionUtils.isEmpty( p_form.getErrors() )){
			Role userRole = (Role) roleDao.getRoleByName( AppConstants.ROLE_USER );
			if(userRole != null){
				User existingUser = userDao.getUserByLogin( p_form.getLogin() );
				if(existingUser != null){
					p_form.getErrors().add( messageSource.getMessage( "message.error.registration.user.exist", new Object[]{p_form.getLogin()}, null ) );
				}else{
					Model model = new Model();
					model = (Model) modelDao.saveOrUpdate( model );
				
					User user = new User(userRole, model, AppConstants.ENTITY_STATE_ENABLED);
			
					user.setLogin( p_form.getLogin());
					user.setPassword( p_form.getPassword1());
					user.setFirstName( p_form.getFirstName());
					user.setLastName( p_form.getLastName());
					user.setEmail( p_form.getEmail() );
					
					userDao.saveOrUpdate( user );
				}
			}else{
				p_form.getErrors().add( messageSource.getMessage( "message.error.role.not.found", null, null ) );
			}
		}
		return p_form;
	}

	

	@Override
	public UserProfileForm initUserProfileForm() throws AppException {
		UserProfileForm form = new UserProfileForm();
		SecurityContext securityContext = SecurityContextHolder.getContext();
		if(securityContext == null || securityContext.getAuthentication() == null || securityContext.getAuthentication().getPrincipal() == null){
			throw new AppException( messageSource.getMessage( "message.error.common", null, null ));
		}
		String login = null;
		Object principal = securityContext.getAuthentication().getPrincipal();
		if(principal instanceof UserDetails){
			login =((org.springframework.security.core.userdetails.User) principal).getUsername();
		}else {
			login = principal.toString();
		}
		
		if(StringUtils.isEmpty( login )){
			throw new AppException( 
					messageSource.getMessage( "message.error.login.empty", null, null ) );	
		}
		User user = userDao.getUserByLogin( login );
		if(user == null){
			throw new AppException( 
					messageSource.getMessage( "message.error.user.not.found.by.login", new Object[]{login} , null ) );
		}
		
		//set user details for user profile form
		form.setId( user.getId() );
		form.setEnabled( user.getEnabled() );
		form.setLogin( user.getLogin() );
		form.setFirstName( user.getFirstName() );
		form.setLastName( user.getLastName() );
		form.setEmail( user.getEmail() );
		
		return form;
	}

	
	@Override
	public UserProfileForm saveUserProfile(UserProfileForm p_form) throws AppException {
		validateUserProfileForm( p_form );
		if(CollectionUtils.isEmpty( p_form.getErrors() )){
			//find user by id
			User user =  (User) userDao.getById( User.class, p_form.getId() );
			if(user == null){
				throw new AppException( 
					messageSource.getMessage( "message.error.user.not.found.by.id", new Object[]{p_form.getId()} , null ) );
			}
			
			//check for correct old password
			if(!user.getPassword().equals( p_form.getOldPassword() )){
				p_form.getErrors().add( messageSource.getMessage( "message.error.pld.password.invalid", null, null ) );
			}
			//check for already existing user with same login
			User existingUser = userDao.getUserByLogin( p_form.getLogin() );
			if(existingUser != null && !existingUser.getId().equals( user.getId() )){
				p_form.getErrors().add( messageSource.getMessage( "message.error.registration.user.exist", new Object[]{p_form.getLogin()}, null ) );
			}
			if(CollectionUtils.isEmpty( p_form.getErrors() )){
				//set new user details
				user.setLogin( p_form.getLogin());
				user.setPassword(StringUtils.isNotBlank(p_form.getPassword1()) ? p_form.getPassword1() : p_form.getOldPassword());
				user.setFirstName( p_form.getFirstName());
				user.setLastName( p_form.getLastName());
				user.setEmail( p_form.getEmail() );

				userDao.saveOrUpdate( user );
			}
		}
		return p_form;
	}
	
	/**
	 * Validates user login <br>
	 * @param p_form - {@link RegistrationForm} or {@link UserProfileForm}
	 */
	private void validateLogin(RegistrationForm p_form){
		//validate login
		String login = p_form.getLogin();
		if(StringUtils.isBlank( login )){
			p_form.getErrors().add( messageSource.getMessage( "message.error.field.empty", 
					new Object[]{messageSource.getMessage( "label.form.login", null, null )}, null ) );
		}else if(StringUtils.trim( login ).length() <= AppConstants.REGISTRATION_FIELD_MIN_LENGTH){
			p_form.getErrors().add( messageSource.getMessage( "message.error.field.short", 
					new Object[]{messageSource.getMessage( "label.form.login", null, null ), AppConstants.REGISTRATION_FIELD_MIN_LENGTH}, null ) );
		}else{
			p_form.setLogin( StringUtils.trim( login ) );
		}
	}
	
	/**
	 * Validates user profile form <br>
	 * @param p_form - {@link UserProfileForm}
	 */
	private void validateOldPassword(UserProfileForm p_form){
		String oldPassword = p_form.getOldPassword();
		if(StringUtils.isBlank( oldPassword )){
			p_form.getErrors().add( messageSource.getMessage( "message.error.field.empty", 
					new Object[]{messageSource.getMessage( "label.form.password1", null, null )}, null ) );
		}else if(StringUtils.trim( oldPassword ).length() <= AppConstants.REGISTRATION_FIELD_MIN_LENGTH){
			p_form.getErrors().add( messageSource.getMessage( "message.error.field.short", 
					new Object[]{messageSource.getMessage( "label.form.password1", null, null ), AppConstants.REGISTRATION_FIELD_MIN_LENGTH}, null ) );
		}else{
			p_form.setOldPassword( StringUtils.trim( oldPassword ) );
		}
	}
	
	/**
	 * Validate passwords
	 * @param p_form - {@link RegistrationForm} or {@link UserProfileForm}
	 */
	private void validatePasswords(RegistrationForm p_form, Boolean mode){
		String password1 = p_form.getPassword1();
		String password2 = p_form.getPassword2();
		
		if(mode.equals( MODE_USERPROFILE )){
			if(StringUtils.isBlank( password1 ) && StringUtils.isBlank( password2 )){
				return;
			}
		}
		
		//validate password
		if(StringUtils.isBlank( password1 )){
			p_form.getErrors().add( messageSource.getMessage( "message.error.field.empty", 
					new Object[]{messageSource.getMessage( "label.form.password1", null, null )}, null ) );
		}else if(StringUtils.trim( password1 ).length() <= AppConstants.REGISTRATION_FIELD_MIN_LENGTH){
			p_form.getErrors().add( messageSource.getMessage( "message.error.field.short", 
					new Object[]{messageSource.getMessage( "label.form.password1", null, null ), AppConstants.REGISTRATION_FIELD_MIN_LENGTH}, null ) );
		}else{
			p_form.setPassword1( StringUtils.trim( password1 ) );
		}
		
		//validate password confirmation
		if(StringUtils.isBlank( password2 )){
			p_form.getErrors().add( messageSource.getMessage( "message.error.field.empty", 
					new Object[]{messageSource.getMessage( "label.form.password2", null, null )}, null ) );
		}else if(StringUtils.trim( password2 ).length() <= AppConstants.REGISTRATION_FIELD_MIN_LENGTH){
			p_form.getErrors().add( messageSource.getMessage( "message.error.field.short", 
					new Object[]{messageSource.getMessage( "label.form.password2", null, null ), AppConstants.REGISTRATION_FIELD_MIN_LENGTH}, null ) );
		}else{
			p_form.setPassword2( StringUtils.trim( password2 ) );
		}
		
		if(!p_form.getPassword1().equals( p_form.getPassword2() )){
			p_form.getErrors().add( messageSource.getMessage( "message.error.field.password.diff", null, null ) );
		}
	}
	
	/**
	 * Validate first name of user <br>
	 * @param p_form - {@link RegistrationForm} or {@link UserProfileForm}
	 */
	private void validateFirstName(RegistrationForm p_form){
		//validate first name
		String fisrtName = p_form.getFirstName();
		if(StringUtils.isBlank( fisrtName )){
			p_form.getErrors().add( messageSource.getMessage( "message.error.field.empty", 
					new Object[]{messageSource.getMessage( "label.form.first.name", null, null )}, null ) );
		}else{
			p_form.setFirstName( StringUtils.trim( fisrtName ) );
		}
	}
	
	/**
	 * Validate last name of user<br>
	 * @param p_form - {@link RegistrationForm} or {@link UserProfileForm}
	 */
	private void validateLastName(RegistrationForm p_form){
		//validate last name
		String lastName = p_form.getLastName();
		if(StringUtils.isBlank( lastName )){
			p_form.getErrors().add( messageSource.getMessage( "message.error.field.empty", 
					new Object[]{messageSource.getMessage( "label.form.last.name", null, null )}, null ) );
		}else{
			p_form.setLastName( StringUtils.trim( lastName ) );
		}
	}
	
	/**
	 * Validate user email <br>
	 * @param p_form - {@link RegistrationForm} or {@link UserProfileForm}
	 */
	private void validateEmail(RegistrationForm p_form){
		//validate email
		String email = p_form.getEmail();
		if(StringUtils.isBlank( email )){
			p_form.getErrors().add( messageSource.getMessage( "message.error.field.empty", 
					new Object[]{messageSource.getMessage( "label.form.email", null, null )}, null ) );
		}else{
			p_form.setEmail( StringUtils.trim( email ) );
		}
	}
	
	/**
	 * Validates all fields of {@link RegistrationForm} before registering new user <br>
	 * @param p_form - {@link RegistrationForm}
	 */
	private void validateRegistrationForm(RegistrationForm p_form){
		if(CollectionUtils.isNotEmpty( p_form.getErrors() )){
			p_form.getErrors().clear();
		}
		validateLogin( p_form );
		validatePasswords( p_form, MODE_REGISTRATION );
		validateFirstName( p_form );
		validateLastName( p_form );
		validateEmail( p_form );
	}
	
	private void validateUserProfileForm(UserProfileForm p_form){
		if(CollectionUtils.isNotEmpty( p_form.getErrors() )){
			p_form.getErrors().clear();
		}
		validateLogin( p_form );
		validateOldPassword( p_form );
		validatePasswords( p_form, MODE_USERPROFILE );
		validateFirstName( p_form );
		validateLastName( p_form );
		validateEmail( p_form );
	}
}
